<template>
	<view :id="elId" class="tn-rate-class tn-rate" @touchmove.stop.prevent="touchMove">
		<view class="tn-rate__wrap" :class="[elClass]" v-for="(item, index) in count" :key="index">
			<view class="tn-rate__wrap__icon"
				:class="[`tn-icon-${(allowHalf && halfIcon ? activeIndex > index + 1 : activeIndex > index) ? elActionIcon : elInactionIcon}`]"
				:style="[iconStyle(index)]" @tap="click(index + 1, $event)">
				<!-- 半图标 -->
				<view v-if="showHalfIcon(index)" class="tn-rate__wrap__icon--half" :class="[`tn-icon-${elActionIcon}`]"
					:style="[halfIconStyle]"></view>
			</view>
		</view>
	</view>
</template>

<script>
	export default {
		name: 'tn-rate',
		props: {
			// 选中星星的数量
			value: {
				type: Number,
				default: 0
			},
			// 显示的星星数
			count: {
				type: Number,
				default: 5
			},
			// 最小能选择的星星数
			minCount: {
				type: Number,
				default: 0
			},
			// 禁用状态
			disabled: {
				type: Boolean,
				default: false
			},
			// 是否可以选择半星
			allowHalf: {
				type: Boolean,
				default: false
			},
			// 星星大小
			size: {
				type: Number,
				default: 32
			},
			// 被选中的图标
			activeIcon: {
				type: String,
				default: 'star-fill'
			},
			// 未被选中的图标
			inactiveIcon: {
				type: String,
				default: 'star'
			},
			// 被选中的颜色
			activeColor: {
				type: String,
				default: '#01BEFF'
			},
			// 默认颜色
			inactiveColor: {
				type: String,
				default: '#AAAAAA'
			},
			// 星星之间的距离
			gutter: {
				type: Number,
				default: 10
			},
			// 自定义颜色
			colors: {
				type: Array,
				default () {
					return []
				}
			},
			// 自定义图标
			icons: {
				type: Array,
				default () {
					return []
				}
			}
		},
		computed: {
			// 图标显示的比例
			showHalfIcon(index) {
				return index => {
					return this.allowHalf && Math.ceil(this.activeIndex) === index + 1 && this.halfIcon
				}
			},
			// 被激活的图标
			elActionIcon() {
				const len = this.icons.length
				// icons参数传递了3个图标，当选中2个时，用第一个图标，4个时，用第二个图标
				// 5个时，用第三个图标作为激活的图标
				if (len && len <= this.count) {
					const step = Math.round(Math.ceil(this.activeIndex) / Math.round(this.count / len))
					if (step < 1) return this.icons[0]
					if (step > len) return this.icons[len - 1]
					return this.icons[step - 1]
				}
				return this.activeIcon
			},
			// 未被激活的图标
			elInactionIcon() {
				const len = this.icons.length
				// icons参数传递了3个图标，当选中2个时，用第一个图标，4个时，用第二个图标
				// 5个时，用第三个图标作为激活的图标
				if (len && len <= this.count) {
					const step = Math.round(Math.ceil(this.activeIndex) / Math.round(this.count / len))
					if (step < 1) return this.icons[0]
					if (step > len) return this.icons[len - 1]
					return this.icons[step - 1]
				}
				return this.inactiveIcon
			},
			// 被激活的颜色
			elActionColor() {
				const len = this.colors.length
				// 如果有设置colors参数(此参数用于将图标分段，比如一共5颗星，colors传3个颜色值，那么根据一定的规则，2颗星可能为第一个颜色
				// 4颗星为第二个颜色值，5颗星为第三个颜色值)
				if (len && len <= this.count) {
					const step = Math.round(Math.ceil(this.activeIndex) / Math.round(this.count / len))
					if (step < 1) return this.colors[0]
					if (step > len) return this.colors[len - 1]
					return this.colors[step - 1]
				}
				return this.activeColor
			},
			// 图标的样式
			iconStyle() {
				return index => {
					let style = {}

					style.fontSize = this.$tn.string.getLengthUnitValue(this.size)
					style.padding = `0 ${this.$tn.string.getLengthUnitValue(this.gutter / 2)}`
					// 当前图标的颜色
					if (this.allowHalf && this.halfIcon) {
						style.color = this.activeIndex > index + 1 ? this.elActionColor : this.inactiveColor
					} else {
						style.color = this.activeIndex > index ? this.elActionColor : this.inactiveColor
					}
					return style
				}
			},
			// 半图标样式
			halfIconStyle() {
				let style = {}

				style.fontSize = this.$tn.string.getLengthUnitValue(this.size)
				style.padding = `0 ${this.$tn.string.getLengthUnitValue(this.gutter / 2)}`
				style.color = this.elActionColor
				return style
			}
		},
		data() {
			return {
				// 保证控件的唯一性
				elId: this.$tn.uuid(),
				elClass: this.$tn.uuid(),
				// 评分盒子左边到屏幕左边的距离，用于滑动选择时计算距离
				starBoxLeft: 0,
				// 当前激活的星星的序号
				activeIndex: this.value,
				// 每个星星的宽度
				starWidth: 0,
				// 每个星星最右边到盒子组件最左边的距离
				starWidthArr: [],
				// 标记是否为半图标
				halfIcon: false,
			}
		},
		watch: {
			value: {
				handler(newName, oldName) {
					this.activeIndex = newName
					if (this.allowHalf && (newName % 1 === 0.5)) {
						this.halfIcon = true
					} else {
						this.halfIcon = false
					}
				},
				immediate: true
			},
			size() {
				// 当尺寸修改的时候重新获取布局尺寸信息
				this.$nextTick(() => {
					this.getElRectById()
					this.getElRectByClass()
				})
			}
		},
		mounted() {
			this.getElRectById()
			this.getElRectByClass()
		},
		methods: {
			// 获取评分组件盒子的布局信息
			getElRectById() {
				this._tGetRect('#' + this.elId).then(res => {
					this.starBoxLeft = res.left
				})
			},
			// 获取单个星星的尺寸
			getElRectByClass() {
				this._tGetRect('.' + this.elClass).then(res => {
					this.starWidth = res.width
					// 把每个星星最右边到盒子最左边的距离
					for (let i = 0; i < this.count; i++) {
						this.starWidthArr[i] = (i + 1) * this.starWidth
					}
				})
			},
			// 手指滑动
			touchMove(e) {
				if (this.disabled) return
				if (!e.changedTouches[0]) return

				const movePageX = e.changedTouches[0].pageX
				// 滑动点相对于评分盒子左边的距离
				const distance = movePageX - this.starBoxLeft

				// 如果滑动到了评分盒子的左边界，设置为0星
				if (distance <= 0) {
					this.activeIndex = 0
				}

				// 计算滑动的距离相当于点击多少颗星星
				let index = Math.ceil(distance / this.starWidth)
				if (this.allowHalf) {
					const iconHalfWidth = this.starWidthArr[index - 1] - (this.starWidth / 2)
					if (distance < iconHalfWidth) {
						this.halfIcon = true
						index -= 0.5
					} else {
						this.halfIcon = false
					}
				}
				this.activeIndex = index > this.count ? this.count : index

				if (this.activeIndex < this.minCount) this.activeIndex = this.minCount

				this.emitEvent()
			},
			// 通过点击直接选中
			click(index, e) {
				if (this.disabled) return
				// 半星选择
				if (this.allowHalf) {
					if (!e.changedTouches[0]) return
					const movePageX = e.changedTouches[0].pageX
					// 点击点相对于当前图标左边的距离
					const distance = movePageX - this.starBoxLeft
					const iconHalfWidth = this.starWidthArr[index - 1] - (this.starWidth / 2)
					if (distance < iconHalfWidth) {
						this.halfIcon = true
					} else {
						this.halfIcon = false
					}
				}

				// 对第一个星星特殊处理，只有一个的时候，点击可以取消，否则无法作0星评价
				if (index == 1) {
					if (this.allowHalf && this.allowHalf) {
						if ((this.activeIndex === 0.5 && this.halfIcon) ||
							(this.activeIndex === 1 && !this.halfIcon)) {
							this.activeIndex = 0
						} else {
							this.activeIndex = this.halfIcon ? 0.5 : 1
						}
					} else {
						if (this.activeIndex == 1) {
							this.activeIndex = 0
						} else {
							this.activeIndex = 1
						}
					}
				} else {
					this.activeIndex = (this.allowHalf && this.halfIcon) ? index - 0.5 : index
				}

				if (this.activeIndex < this.minCount) this.activeIndex = this.minCount

				this.emitEvent()
			},
			// 发送事件
			emitEvent() {
				this.$emit('change', this.activeIndex)
				// 修改v-model的值
				this.$emit('input', this.activeIndex)
			}
		}
	}
</script>

<style lang="scss" scoped>
	.tn-rate {
		display: inline-flex;
		align-items: center;
		margin: 0;
		padding: 0;

		&__wrap {

			&__icon {
				position: relative;
				box-sizing: border-box;

				&--half {
					position: absolute;
					top: 0;
					left: 0;
					display: inline-block;
					overflow: hidden;
					width: 50%;
				}
			}
		}
	}
</style>
